package com.beiding.my.mql;

import com.beiding.my.bemapping.BeTypeMapper;
import com.beiding.my.mql.utils.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.persistence.Query;
import java.util.*;

@Component
public class MQLBuilder {

    /*
       mql是一种伪sql语法
       支持多表关联查询和返回

       例如:
           select a.name,a.b1.name,a.b2.name from StudentData a where a.b1.name= :p1

       自动编译 => SELECT a.name AS f1,b1.name AS f2,b2.name AS f3 FROM student_be a,teacher_be b1,teacher_be b2,_quote_teacher_be q1,_quote_teacher_be q2 WHERE a.id = q1.`_student_be_a` AND b1.`id`=q1.`self` AND a.`id`=q2.`_student_be_b` AND b2.`id`=q2.`self` and b1.name = :p1

       //必须有类型声明

        a.b

        B被A引用,将可以得到引用B的详情:
            中间表的表名,B被A引用在中间表中的列名,以及指向B的self字段在中间表中的列名,以及A的id字段在A表中的列名,B的id字段在表中的列名

     */

    @Autowired
    private NativeSql nativeSql;

    private Map<String, MQLCompiler> mqlCompilerMap = new HashMap<>();

    private Map<String, ExecutorFactory> executorFactoryHashMap = new HashMap<>();

    public MQLBuilder(List<MQLCompiler> mqlCompilers, List<ExecutorFactory> executorFactories) {
        for (MQLCompiler compiler : mqlCompilers) {
            mqlCompilerMap.put(compiler.getCommand(), compiler);
        }
        for (ExecutorFactory factory : executorFactories) {
            executorFactoryHashMap.put(factory.name(), factory);
        }
    }

    private Map<String, MQLExecutor> mqlExecutorMap = new HashMap<>();

    public MQLExecutor build(String mql) {

        MQLExecutor r = mqlExecutorMap.get(mql);

        if (r == null) {
            synchronized (this) {
                if ((r = mqlExecutorMap.get(mql)) == null) {

/*

                    if (mql != new String(mql).intern()) {
                        throw new Error("mql必须是静态字符串");
                    }
*/

                    String t = mql.trim();

                    //TODO 根据第一个 mql区段决定  Executor的类型
                    CompilerDetails compilerDetails = deepHandle(t);

                    ExecutorFactory executorFactory = executorFactoryHashMap.get(compilerDetails.type);
                    r = executorFactory.create(compilerDetails.sql, compilerDetails.beTypeMapper);

                    //放入缓存
                    mqlExecutorMap.put(mql, r);
                }
            }
        }
        return r;
    }


    //递归处理
    //深度处理
    private CompilerDetails deepHandle(String mql) {

        //最外层

        //屏蔽掉子句
        SubMqlHide subMqlHide = MqlUtils.hideSubMql(mql);

        //被屏蔽的所有子句
        List<Block> hindBlocks = subMqlHide.getHindBlocks();

        Map<Pair, String> rep = new HashMap<>();
        Map<String, String> placeHolderMap = new HashMap<>();
        for (Block hindBlock : hindBlocks) {
            String holder = createHolder();
            rep.put(hindBlock, holder);

            //处理所有子句
            placeHolderMap.put(holder, deepHandle(hindBlock.getText()).sql);
        }

        //屏蔽之后的结果
        String result = subMqlHide.getResult();

        //将带有占位符的结果注入回去
        result = MqlUtils.recover(result, rep);

        //进行裁切处理

        //裁切成多个区段
        List<KeywordBlock> keywordBlocks = MqlUtils.splitMql(result);

        Set<String> types = new HashSet<>();

        String type = null;

        List<String> list = new ArrayList<>();

        for (KeywordBlock block : keywordBlocks) {
            types.add(block.getKeyword());
            type = block.getKeyword();

            //添加所有的文本进去
            list.add(block.getText());
        }


        if (types.size() != 1) {
            throw new MqlException("不能识别的mql类型:" + mql);
        }


        //找到对应的编译器,进行编译
        MQLCompiler mqlCompiler = mqlCompilerMap.get(type);
        if (mqlCompiler == null) {
            throw new NoSuchCommandException(type, mql);
        }

        //获取编译的结果
        CompilerDetails details = mqlCompiler.compile(list);

        //替换会占位符
        for (Map.Entry<String, String> stringStringEntry : placeHolderMap.entrySet()) {
            details.sql = details.sql.replace(stringStringEntry.getKey(), stringStringEntry.getValue());
        }

        return details;

    }

    private static int h = 1;

    private static String createHolder() {
        return "____________________" + h++ + "____________________";
    }


}
